﻿using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;

namespace NFox.Pyeditor.Runtime
{
    sealed class BreakPointMargin : AbstractMargin
    {

        CodeEditor _code;
        Brush _background;
        const int _margin = 20;

        public BreakPointList BreakPoints { get; } = new BreakPointList();

        public BreakPointMargin(CodeEditor code)
        {
            Width = _margin;
            _code = code;
            _code.Document.Changing += Document_Changing;
            LinearGradientBrush background = new LinearGradientBrush();
            background.StartPoint = new Point(0, 0);
            background.EndPoint = new Point(1, 0);
            background.GradientStops.Add(new GradientStop(Colors.Black, 0));
            background.GradientStops.Add(new GradientStop(Colors.Gray, 0.1));
            background.GradientStops.Add(new GradientStop(Colors.Black, 1));
            _background = background;
            _code.TextArea.TextView.ScrollOffsetChanged += delegate { InvalidateVisual(); };
        }

        private int GetEnterCount(string txt)
        {
            int count = 0;
            for (int i = 0; (i = txt.IndexOf("\r\n", i)) != -1; i += 2)
                count++;
            return count;
        }

        private bool IsNullLine(int line)
        {
            var dl = _code.Document.GetLineByNumber(line);
            var txt = _code.Document.GetText(dl);
            return txt.Trim().Length == 0;
        }

        private void Document_Changing(object sender, ICSharpCode.AvalonEdit.Document.DocumentChangeEventArgs e)
        {

            int line = _code.Document.GetLineByOffset(e.Offset).LineNumber;
            int number = 0;
            if (e.RemovalLength > 0)
            {
                var txt = e.RemovedText.Text;
                if (txt.Contains("\r\n"))
                {
                    number = GetEnterCount(txt);
                    int endline = line + number;
                    BreakPoints.Remove(bt => bt.Line > line && bt.Line <= endline);
                }
                else
                {
                    BreakPoints.Remove(bt => bt.Line == line && IsNullLine(line));
                }
            }

            number = -number;
            if(e.InsertionLength > 0)
            {
                var txt = e.InsertedText.Text;
                if (txt.Contains("\r\n"))
                    number += GetEnterCount(txt);
            }

            if(number != 0)
            {
                foreach(var bt in BreakPoints)
                {
                    if (bt.Line > line)
                        bt.Line += number;
                }
            }
            InvalidateVisual();
        }



        protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
        {
            return new PointHitTestResult(this, hitTestParameters.HitPoint);
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            return new Size(_margin, 0);
        }

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);
            var pos = e.GetPosition(this);
            pos.Y = Math.Max(Math.Min(pos.Y, TextView.ActualHeight), 0);
            pos.Y += TextView.VerticalOffset;
            VisualLine vl = TextView.GetVisualLineFromVisualTop(pos.Y);
            if (vl == null) return;
            TextLine tl = vl.GetTextLineByVisualYPosition(pos.Y);
            var line = vl.FirstDocumentLine.LineNumber;
            var bt = new BreakPoint(_code.Module.File.FullName, vl.FirstDocumentLine.LineNumber);
            if (IsNullLine(line)) return;
            if (BreakPoints.Contains(bt))
                BreakPoints.Remove(bt);
            else
                BreakPoints.Add(bt);
            InvalidateVisual();
        }

        protected override void OnRender(DrawingContext dc)
        {
            var size = RenderSize;
            if (size.Width < 1)
                return;

            size.Width -= 2;
            dc.DrawRectangle(_background, null, new Rect(size));

            foreach (var bp in BreakPoints)
            {
                var y = (bp.Line - 1) * TextView.DefaultLineHeight - TextView.VerticalOffset;
                if (y >= -20 && y < RenderSize.Height + 20)
                    dc.DrawEllipse(Brushes.OrangeRed, new Pen(Brushes.Yellow, 1), new Point(9, y + 8), 5, 5);
            }
        }


    }
}

